//
// Created by denglibin on 19-10-22.
//

/**
 *守护进程
 **/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>

/**
 * 创建守护进程
一.守护进程的定义
1.守护进程是脱离于终端并且在后台运行的进程
2.守护进程脱离终端是为了避免在执行过程中的信息在任何终端上显示，并且不被任何终端产生的终端信息所打断
3.守护进程通常在系统引导装入时启动

二.守护进程的作用
 1.守护进程是一个生存周期较长的进程，通常独立于控制终端并且周期性的执行某种任务或者等待处理某些待发生的事件
2.大多数服务都是通过守护进程实现的
3.关闭终端，相应的进程都会被关闭，而守护进程却能够突破这种限制


三.守护进程的创建过程
背景知识：
1. 进程组：一个或多个进程的集合，进程组由进程组ID标识，进程组长的进程ID和进程组ID一致，并且进程组ID不会由于进程组长的退出而受到影响
2. 会话周期：一个或多个进程组的集合，比如用户从登陆到退出，这个期间用户运行的所有进程都属于该会话周期
3.setsid函数：创建一个新会话，并担任该会话组的组长，调用setsid函数的目的：让进程摆脱原会话，原进程组，原终端的控制

 创建守护进程的过程：

1.创建子进程，父进程退出
子进程变成孤儿进程，然后由1号init进程收养

2.子进程创建新会话
调用setsid创建新的会话，摆脱原会话，原进程组，原终端的控制，自己成为新会话的组长

3.将当前目录改为根目录
正在运行的进程文件系统不能卸载，如果目录要回退，则此时进程不能做到，为了避免这种麻烦，以根目录为当前目录

4.重设文件权限掩码
子进程的文件权限掩码是复制的父进程的，不重新设置的话，会给子进程使用文件带来诸多麻烦

5.关闭不需要的文件描述符
子进程的文件描述符也是从父进程复制来的，那些不需要的文件描述符永远不会被守护进程使用，会白白的浪费系统资源，还可能导致文件系统无法结束
 */
static void my_daemon(){
    pid_t  pid = fork();
    if(pid < 0){
        perror("fork:");
        exit(-1);
    } else if (pid > 0){ // 父进程
        exit(1);
    }
    /** 1、子进程创建会话*/
    pid = setsid(); // setid()调用将返回新创建的进程组ID，并且该进程组ID等同于调用进程的进程ID。失败则返回-1，并设置errno的值指出错误原因。
    if(pid < 0){
        perror("setsic:");
        exit(-1);
    }
    /**2、改变工作目录（一般为根目录）使得进程不与任何文件系统联系*/
    if(chdir("/") < 0){
        perror("chdir:");
        exit(-1);
    }

    /**3、权限设置*/
    umask(0022);// umask函数为进程设置文件模式创建屏蔽字，并返回以前的值。四个数字代表是赋值初始化准备丢弃的权限
       /*第一个0代表suid 丢弃的权限；
    　　第二个0代表本文件/目录拥有者什么权限都没丢弃（如果是文件，x权限除外，为什么看看上面解释）；
    　　第三个2代表本文件/目录的用户组丢弃了w权限（如果是文件那么它的x权限也丢弃）；
    　　第三个2代表本文件/目录的其他用户能使用的权限只有有r和x（文件除外）。
    　　综上：如果新创建一个文件那么它的权限是：-wr-w--w-- 即是：644
        */
   /**4、关闭相关文件描述符*/
    /*int des=getdtablesize();
    for(int i=0;i<des;i++){
        close(i);
    }*/
    close(STDIN_FILENO);

    int fd = open("/dev/null", O_RDWR);
    if(fd == -1){
        perror("open:");
        exit(-1);
    }
    // 重定向文件描述符
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    while (1); // 防止进程退出（其他逻辑）
}
static int main_(void){
    my_daemon();
    return 0;
}